import java.util.Enumeration;

/**
 * FastHashtable.java
 * 
 * This class provides storage of key-value pairs. Please note that this class
 * does not implement java.util.Map.
 * 
 * You can use the hashCode() implemented in each of the Java objects. For
 * example, PA4 uses String object as key. Therefore, you can use the hashCode()
 * method provided by java.lang.String.
 * 
 * Your FastHashtable should be able to manage collisions and rehash. You should
 * choose an algorithm(s) to manage this collision. You can use single algorithm
 * or mix multiple algorithms. Of course, you can also come up with your own
 * algorithm.
 * 
 * Use of Java’s Hashtable class and Hashmap class are NOT ALLOWED for PA4.
 * Please make sure that your software does not include java.util.Hashtable or
 * java.util.HashMap in any case.
 */

public class FastHashtable<K, V> {

	private int initialCapacity; // Initial Capacity
	private double loadFactor; // Load Factor
	private int count;
	private int threshold;
	private K key;
	private V value;
	private FastEntryArry table;

	public FastHashtable() {
		// Constructs a new, empty hashtable with a default initial capacity and
		// load factor
		this(11, 0.75f);
	}

	public FastHashtable(int _initialCapacity) {
		// Constructs a new, empty hashtable with the specified initial capacity
		// and default load factor.
		this(_initialCapacity, 0.75f);
	}

	public FastHashtable(int initialCapacity, float loadFactor) {
		// Constructs a new, empty hashtable with the specified initial capacity
		// and the specified load factor.
		if (initialCapacity < 0)
			throw new IllegalArgumentException("Illegal Capacity: "
					+ initialCapacity);
		if (loadFactor <= 0 || Float.isNaN(loadFactor))
			throw new IllegalArgumentException("Illegal Load: " + loadFactor);

		if (initialCapacity == 0)
			initialCapacity = 1;
		this.loadFactor = loadFactor;
		this.initialCapacity = initialCapacity;
		this.count = 0;
		this.threshold = (int) (initialCapacity * loadFactor);

	}

	public int size() {
		return count;
	}

	public void add(K key, V value) {
		// Maps the specified key to the specified value in this hashtable.
		if (count < threshold) {
			int hash = hashcode(key);
			FastEntry<K, V> fastentry = new FastEntry<K, V>(hash, key, value);
			this.table.add(fastentry);
			count++;
		} else {
			rehash();
			add(key, value);
		}
	}

	public boolean remove(Object key) {
		// Removes the key (and its corresponding value) from this hashtable.
		return this.table.remove(key);
	}

	public boolean containsKey(Object key) {
		// Tests if the specified object is a key in this hashtable.
		return (table.search(key) > 0);

	}

	public Object retrieve(Object key) {
		// Returns an object stored with the specified key
		return this.table.retrieve(key);
	}

	public Enumeration getKeys() {
		// Returns an enumeration of the keys in this hashtable.
		return null;

	}

	public double getLoadFactor() {
		// Returns current value of load factor for your debugging.
		return this.loadFactor;

	}

	protected void rehash() {
		// Increases the capacity of and internally reorganizes
		// this hashtable, in order to accommodate and access
		// its entries more efficiently.
		// Whenever your adding operation causes higher load factor than
		// the limit of load factor, you should call this method in your add()
		// method.
		int oldCapacity = this.count;
		FastEntryArry oldMap = this.table;
		int newCapacity = oldCapacity * 2 + 1;
		threshold = (int) (initialCapacity * loadFactor);

	}

	public int hashcode(Object key) {
		return key.hashCode();
	}

	// Feel free to add your main() method for your unit test

}
